﻿using System;
using Csu.Modsim.ModsimModel; 
using ASquared.ModelOptimization; 
using ASquared.SymbolicMath; 

namespace iwpm.optimization
{
    public class GrandCouleeTDGMapper : LinearMapper
    {
        private Model _model;
        private Node _gcl;
        private Link _gclSpill;
        private Link[] _gclInf, _gclOut, _gclDwnOut;
        private double _conversionFactor; 
         
        private string[] _varNames = new string[] {
            "FB_Elev", "TW_Elev", 
            "GCL_Inf", "GCL_Out", 
            "GCL_Spill", "GCL_Spill^2"}; 

        public GrandCouleeTDGMapper(Model model)
        {
            _model = model;
            _gcl = model.FindNode("GCL");
            _gclInf = new Link[] { model.FindLink("GCL_Inflow_GCL"), model.FindLink("BNK_GCL") };
            _gclOut = new Link[] { model.FindLink("GCL_Spill"), model.FindLink("GCL_PwrRel"), model.FindLink("GCL_BNK") };
            _gclDwnOut = new Link[] { _gclOut[0], _gclOut[1] }; 
            _gclSpill = _gclOut[0];
            ModsimUnits _inputUnits = new ModsimUnits("cfs"); // actually kcfs
            _conversionFactor = _model.FlowUnits.ConvertFrom(1000.0, _inputUnits); 
        }

        public int SampleIndex 
        {
            get
            {
                return _model.mInfo.CurrentModelTimeStepIndex;
            }
        }

        public String[] Variables
        {
            get
            {
                return _varNames;
            }
        }

        public Symbol[] Symbols 
        {
            get
            {
                // Get the parameter values for determining elevation
                double totalOut = totalFlow(_gclOut); 
                double totalDwnOut = totalFlow(_gclDwnOut); 
                double totalIn = totalFlow(_gclInf);
                double netOut = totalOut - totalIn; 
                Symbol totalOut_Symbols = totalFlow_Symbols(_gclOut);
                Symbol totalDwnOut_Symbols = totalFlow_Symbols(_gclDwnOut); 
                Symbol totalIn_Symbols = totalFlow_Symbols(_gclInf); 
                Symbol netOut_Symbols = totalOut_Symbols - totalIn_Symbols; 

                // forebay elevation
                Symbol fb_elev = _gcl.ElevationFunction(netOut, _model.ScaleFactor, false)
                    .Subs("q", netOut_Symbols);

                // tailwater elevation 
                Symbol tw_elev = _gcl.TailwaterElevationFunction(totalDwnOut, 
                    _model.ScaleFactor, _model.mInfo.MonthIndex).Subs("q", totalDwnOut_Symbols); 

                // inflow 
                Symbol inf = totalIn_Symbols / _conversionFactor; 

                // outflow 
                Symbol outflow = totalOut_Symbols / _conversionFactor; 

                // spill and spill^2 
                Symbol spill = flow_Symbol(_gclSpill) / _conversionFactor;

                return new Symbol[] { fb_elev, tw_elev, inf, outflow, spill, (spill ^ 2) };
            }
        }

        public double[] Values
        {
            get
            {
                // forebay elevation
                double fb_elev = _gcl.mnInfo.avg_elevation; 

                // tailwater elevation 
                double tw_elev = _gcl.mnInfo.tail_elevation; 

                // inflow 
                double inf = totalFlow(_gclInf) / _conversionFactor;

                // outflow 
                double outflow = totalFlow(_gclOut) / _conversionFactor;

                // spill and spill^2 
                double spill = _gclSpill.mlInfo.flow / _conversionFactor;

                return new double[] { fb_elev, tw_elev, inf, outflow, spill, Math.Pow(spill, 2) };
            }
        }

        private double totalFlow(Link[] links)
        {
            double sum = 0;
            foreach (Link l in links)
                sum += l.mlInfo.flow;
            return sum / _model.ScaleFactor; 
        }

        private Symbol flow_Symbol(Link link)
        {
            return new Symbol("q_" + link.number); 
        } 

        private Symbol totalFlow_Symbols(Link[] links)
        {
            Symbol sum = null;
            foreach (Link l in links)
                if (sum == null)
                    sum = new Symbol("q_" + l.number); 
                else 
                    sum += new Symbol("q_" + l.number);
            return sum / _model.ScaleFactor; 
        }
    }
}
